﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
using System.IO;
using System.Web.Script.Serialization;
using System.Net;
using System.Text.RegularExpressions;
namespace WeatherAppService
{
    public class WeatherAppService : IWeatherAppService
    {
        public ReturnUsers GetAllUsers()
        {
            ReturnUsers usersToReturn = new ReturnUsers();
            List<User> queryUsers = new List<User>();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];


                    using (var context = new WeatherAppContext())
                    {
                        var admin = context.UserEntities.Where(c => c.UserName.Equals("admin")).FirstOrDefault();
                        bool validPass = PasswordHash.ValidatePassword(pass, admin.Password);
                        
                        if (username.Equals(admin.UserName) && validPass)
                        {
                            var users = context.UserEntities.
                                        OrderBy(u => u.UserId).ToList();

                            foreach (var item in users)
                            {
                                queryUsers.Add(Converter.UserEntityToUser(item));
                            }

                            usersToReturn.Success = "0";
                            usersToReturn.ErrorMessage = "Successfully Retrieved";
                            usersToReturn.Users = queryUsers;
                            return usersToReturn;
                        }
                        else 
                        {
                            usersToReturn.Success = "1";
                            usersToReturn.ErrorMessage = "Unauthorized!";
                            usersToReturn.Users = queryUsers;
                            return usersToReturn;
                        }
                    }
                }
                catch (Exception e) 
                {
                    usersToReturn.ErrorMessage = e.Message;
                    return usersToReturn;
                }
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                      HttpStatusCode.Unauthorized;
                return usersToReturn;
            }
        }

        public ReturnUser AddUser(Stream JSONdataStream)
        {
            ReturnUser userToReturn = new ReturnUser();
            try
            {
                // Read in Stream into a string...
                StreamReader reader = new StreamReader(JSONdataStream);
                string JSONdata = reader.ReadToEnd();

                // ..then convert the string into a  single "User" class.
                JavaScriptSerializer jss = new JavaScriptSerializer();

                User user = jss.Deserialize<User>(JSONdata);

                if (user == null)
                {
                    // Error: Couldn't deserialize JSON string into a "User" object
                    userToReturn.Success = "1";
                    userToReturn.ErrorMessage = "Couldn't deserilize Json";
                    return userToReturn;
                }

                else
                {
                    //checking for fields empty / null
                    if (!String.IsNullOrEmpty(user.UserName) && !String.IsNullOrEmpty(user.Password)
                        && !String.IsNullOrEmpty(user.FirstName) && !String.IsNullOrEmpty(user.LastName)
                        && !String.IsNullOrEmpty(user.Email))
                    {
                        if (doesEmailExists(user.Email) || !Validator.IsValidEmail(user.Email))
                        {
                            userToReturn.Success = "2";
                            userToReturn.ErrorMessage = "Email already exists / Invalid email";
                            return userToReturn;
                        }

                        else
                        {
                            //pass validation
                            if (!Validator.IsValidPass(user.Password))
                            {
                                userToReturn.Success = "2";
                                userToReturn.ErrorMessage = "Password is too short";
                                return userToReturn;
                            }

                            //hasing pass
                            string hashpass = PasswordHash.CreateHash(user.Password);
                            user.Password = hashpass;

                            UserEntity newUser = new UserEntity();
                            newUser = Converter.UserToUserEntity(user);
                            using (var context = new WeatherAppContext())
                            {
                                context.UserEntities.Add(newUser);
                                context.SaveChanges();
                            }

                            //Sucess
                            userToReturn.Success = "0";
                            userToReturn.ErrorMessage = "Successfully added user";
                            userToReturn.User = user;
                            return userToReturn;
                        }
                    }
                    else
                    {
                        //missing required field
                        userToReturn.Success = "4";
                        userToReturn.ErrorMessage = "Missing required field";                        
                        return userToReturn;
                    }
                }
            }

            catch (Exception e)
            {
                userToReturn.Success = "3";
                userToReturn.ErrorMessage = e.Message; //"Missing a required field / Username already exits";
                return userToReturn;
            }

        }

        public ReturnPosts GetAllPosts()
        {
            List<Post> returnedPosts = new List<Post>();
            ReturnPosts postsToReturn = new ReturnPosts();
            using (var context = new WeatherAppContext())
            {
                var posts = context.PostEntities.
                    OrderByDescending(u => u.PostTime).ToList();

                foreach (var item in posts)
                {
                    returnedPosts.Add(Converter.PostEntityToPost(item));
                }
            }
            postsToReturn.Success = "0";
            postsToReturn.ErrorMessage = "Successfully retrived";
            postsToReturn.Posts = returnedPosts;
            return postsToReturn;
        }

        public ReturnPosts GetAllPostsByUser(string user)
        {
            List<Post> postList = new List<Post>();
            ReturnPosts postsToReturn = new ReturnPosts();
            using (var context = new WeatherAppContext())
            {
                var queryUser = context.UserEntities.Where(c => c.UserName == user).FirstOrDefault();
                if (queryUser == null)
                {
                    postsToReturn.Success = "1";
                    postsToReturn.ErrorMessage = "No user or posts";
                    return postsToReturn;
                }
                else
                {
                    var posts = context.PostEntities
                        .Where(u => u.UserName.ToUpper() == user.ToUpper())
                        .OrderByDescending(c => c.PostTime).ToList();

                    foreach (var item in posts)
                    {
                        postList.Add(Converter.PostEntityToPost(item));
                    }

                    postsToReturn.Success = "0";
                    postsToReturn.ErrorMessage = "Successfully retrieved";
                    postsToReturn.Posts = postList;
                    return postsToReturn;
                }
            }
        }

        public ReturnUser GetUserByCredentials()
        {
            ReturnUser returnUser = new ReturnUser();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];


                    using (var context = new WeatherAppContext())
                    {
                        var result = context.UserEntities.Where(c => c.UserName == username).FirstOrDefault();

                        if (result == null)
                        {
                            returnUser.Success = "2";
                            returnUser.ErrorMessage = "Incorrect username or password";
                            return returnUser;
                        }

                        else
                        {
                            bool validPass = PasswordHash.ValidatePassword(pass, result.Password);
                            if (validPass)
                            {
                                User user = Converter.UserEntityToUser(result);
                                returnUser.Success = "0";
                                returnUser.ErrorMessage = "Sucessfully retrieved";
                                returnUser.User = user;
                                return returnUser;

                            }
                            else
                            {
                                returnUser.Success = "2";
                                returnUser.ErrorMessage = "Incorrect username or password";
                                return returnUser;
                            }
                        }
                    }
                }

                catch (Exception e)
                {
                    WebOperationContext.Current.OutgoingResponse.StatusCode =
                      HttpStatusCode.Unauthorized;
                    returnUser.Success = "3";
                    returnUser.ErrorMessage = e.Message;
                    return returnUser;
                }

            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                      HttpStatusCode.Unauthorized;
                return returnUser;

            }
        }

        public ReturnUser EditUser(Stream JSONdataStream)
        {
            ReturnUser returnUser = new ReturnUser();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];


                    // Read in Stream into a string...
                    StreamReader reader = new StreamReader(JSONdataStream);
                    string JSONdata = reader.ReadToEnd();

                    // ..then convert the string into a  single "User" class.
                    JavaScriptSerializer jss = new JavaScriptSerializer();

                    User inUser = jss.Deserialize<User>(JSONdata);

                    if (inUser == null)
                    {
                        returnUser.Success = "1";
                        returnUser.ErrorMessage = "Couldn't deserialze the request";
                        return returnUser;
                    }

                    else
                    {
                        using (var context = new WeatherAppContext())
                        {
                            var result = context.UserEntities.Where(c => c.UserName == username).FirstOrDefault();
                            if (result == null)
                            {
                                returnUser.Success = "2";
                                returnUser.ErrorMessage = "Wrong credentials provided";
                                return returnUser;
                            }

                            else
                            {
                                bool validPass = PasswordHash.ValidatePassword(pass, result.Password);
                                if (validPass)
                                {
                                    if (result.Email != inUser.Email) //new email
                                    {
                                        //check valid email or email already exits in database
                                        if (Validator.IsValidEmail(inUser.Email) && !doesEmailExists(inUser.Email)
                                            && Validator.IsValidPass(inUser.Password) && !String.IsNullOrEmpty(inUser.FirstName)
                                            && !String.IsNullOrEmpty(inUser.LastName))
                                        {
                                            string hashPass = PasswordHash.CreateHash(inUser.Password);
                                            inUser.Password = hashPass;

                                            result.Email = inUser.Email;
                                            result.FirstName = inUser.FirstName;
                                            result.LastName = inUser.LastName;
                                            result.Password = inUser.Password;

                                            context.SaveChanges(); //saving

                                            returnUser.Success = "0";
                                            returnUser.ErrorMessage = "Sucessfully edited user details";
                                            returnUser.User = Converter.UserEntityToUser(result);

                                            return returnUser;
                                        }
                                        else
                                        {
                                            //invalid email
                                            returnUser.Success = "2";
                                            returnUser.ErrorMessage = "Invalid Email/Password provided OR missing a required field";
                                            return returnUser;
                                        }
                                    }
                                    else
                                    {   //remove email check in database
                                        if (Validator.IsValidEmail(inUser.Email) && Validator.IsValidPass(inUser.Password) 
                                            && !String.IsNullOrEmpty(inUser.FirstName) && !String.IsNullOrEmpty(inUser.LastName))
                                        {
                                            string hashPass = PasswordHash.CreateHash(inUser.Password);
                                            inUser.Password = hashPass;

                                            result.Email = inUser.Email;
                                            result.FirstName = inUser.FirstName;
                                            result.LastName = inUser.LastName;
                                            result.Password = inUser.Password;

                                            context.SaveChanges(); //saving

                                            returnUser.Success = "0";
                                            returnUser.ErrorMessage = "Sucessfully edited user details";
                                            returnUser.User = Converter.UserEntityToUser(result);

                                            return returnUser;
                                        }
                                        else
                                        {
                                            //invalid email
                                            returnUser.Success = "2";
                                            returnUser.ErrorMessage = "Invalid Email/Password provided OR missing a required field";
                                            return returnUser;
                                        }
                                    }

                                }
                                else
                                {
                                    //wrong username/pass
                                    returnUser.Success = "2";
                                    returnUser.ErrorMessage = "Wrong credentials provided";
                                    return returnUser;
                                }
                            }
                        }
                    }
                }

                catch (Exception e)
                {
                    returnUser.Success = "3";
                    returnUser.ErrorMessage = e.Message; // "Error with the query or in Server";
                    return returnUser;
                }
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                      HttpStatusCode.Unauthorized;
                return returnUser;
            }
        }

        public ReturnPosts AddPost(Stream JSONdataStream)
        {
            ReturnPosts returnMesage = new ReturnPosts();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];


                    // Read in Stream into a string...
                    StreamReader reader = new StreamReader(JSONdataStream);
                    string JSONdata = reader.ReadToEnd();

                    // ..then convert the string into a  single "Post" class.
                    JavaScriptSerializer jss = new JavaScriptSerializer();

                    Post inPost = jss.Deserialize<Post>(JSONdata);
                    if (inPost == null)
                    {
                        returnMesage.Success = "1";
                        returnMesage.ErrorMessage = "Couldn't deserilize Json";
                        // Error: Couldn't deserialize JSON string 
                        return returnMesage;
                    }

                    using (var context = new WeatherAppContext())
                    {
                        var result = context.UserEntities.Where(c => c.UserName == username).FirstOrDefault();

                        if (result == null)
                        {
                            returnMesage.Success = "2";
                            returnMesage.ErrorMessage = "Incorrect username or password";
                            return returnMesage;
                            //no user
                        }

                        else
                        {
                            bool validPass = PasswordHash.ValidatePassword(pass, result.Password);
                            if (validPass)
                            {
                                PostEntity newPost = new PostEntity();
                                newPost = Converter.PostToPostEntity(inPost);
                                newPost.UserName = username;
                                context.PostEntities.Add(newPost);
                                context.SaveChanges();

                                returnMesage.Success = "0";
                                returnMesage.ErrorMessage = "Sucessfully added post";
                                return returnMesage;

                            }
                            else
                            {
                                returnMesage.Success = "2";
                                returnMesage.ErrorMessage = "Incorrect username or password";
                                return returnMesage;
                                //wrong password
                            }
                        }
                    }

                }

                catch (Exception e)
                {
                    returnMesage.Success = "3";
                    returnMesage.ErrorMessage = e.Message;
                    return returnMesage;
                }
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                     HttpStatusCode.Unauthorized;
                return returnMesage;
            }

        }

        public ReturnUser PassRecovery()
        {
            ReturnUser returnMesage = new ReturnUser();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userEmail = authHead.Split(new char[] { ':' });
                    string username = userEmail[0];
                    string email = userEmail[1];



                    if (username == null && email == null)
                    {
                        WebOperationContext.Current.OutgoingResponse.StatusCode =
                                                        HttpStatusCode.Unauthorized;
                        return returnMesage;
                    }
                    using (var context = new WeatherAppContext())
                    {
                        var result = context.UserEntities.Where(c => c.UserName == username || c.Email == email).FirstOrDefault();

                        if (result == null)
                        {
                            returnMesage.Success = "2";
                            returnMesage.ErrorMessage = "Incorrect username or email";
                            return returnMesage;
                            //no user
                        }

                        else
                        {
                            //send email with new pass
                            string subject = "Password Recovery";
                            string createdPass = PasswordHash.GetUniqueKey(6); // create new pass
                            string body = "<html><body><h1>Hi " +
                                    result.UserName + ",</h1><p>Your new password is&nbsp;<strong>" + createdPass +
                                    "</strong></p><p>Please don&#39;t reply to this message.</p></body></html>";
                            MailHelper.SendMailMessage("SocialWeatherApp@gmail.com", result.Email, subject, body);

                            //hash new pass
                            string hashPass = PasswordHash.CreateHash(createdPass);
                            result.Password = hashPass;
                            context.SaveChanges(); // save in database

                            returnMesage.Success = "0";
                            returnMesage.ErrorMessage = "Password have been sent to the registered email";
                            return returnMesage;
                        }
                    }

                }
                catch (Exception e)
                {
                    returnMesage.Success = "3";
                    returnMesage.ErrorMessage = e.Message;
                    return returnMesage;

                }

            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                     HttpStatusCode.Unauthorized;
                return returnMesage;
            }
        }

        public ReturnPosts DeletePostByID(string postID)
        {
            ReturnPosts returnMesage = new ReturnPosts();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];


                    var inPostId = Convert.ToInt32(postID);
                    using (var context = new WeatherAppContext())
                    {
                        var result = context.PostEntities.Where(c => c.PostId == inPostId).FirstOrDefault();

                        if (result == null)
                        {
                            returnMesage.Success = "1";
                            returnMesage.ErrorMessage = "No post found with that id";
                            return returnMesage;
                            //no post
                        }

                        else
                        {
                            //check whether its admin or user who posted the message is making the request
                            var admin = context.UserEntities.Where(c => c.UserName.Equals("admin")).FirstOrDefault();                           

                            if (result.UserName.Equals(username) || (username.Equals(admin.UserName)))
                            {
                                bool validAdminPass = PasswordHash.ValidatePassword(pass, admin.Password);
                                bool validUserPass = PasswordHash.ValidatePassword(pass, result.User.Password);

                                if (validUserPass || validAdminPass)
                                {
                                    //remove
                                    context.PostEntities.Remove(result);
                                    context.SaveChanges();

                                    returnMesage.Success = "0";
                                    returnMesage.ErrorMessage = "Sucessfully deleted post";
                                    return returnMesage;
                                }
                                else
                                {
                                    WebOperationContext.Current.OutgoingResponse.StatusCode =
                                                                HttpStatusCode.Unauthorized;
                                    returnMesage.Success = "3";
                                    returnMesage.ErrorMessage = "Unauthorized attempt";
                                    return returnMesage;
                                    //wrong password
                                }
                            }
                            else
                            {
                                WebOperationContext.Current.OutgoingResponse.StatusCode =
                                                                   HttpStatusCode.Unauthorized;
                                returnMesage.Success = "3";
                                returnMesage.ErrorMessage = "Unauthorized attempt";
                                return returnMesage;
                            }
                        }

                    }
                }
                catch (Exception e)
                {
                    returnMesage.Success = "3";
                    returnMesage.ErrorMessage = e.Message;
                    return returnMesage;
                }
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                     HttpStatusCode.Unauthorized;
                
                    returnMesage.ErrorMessage="no valid header";
                    return returnMesage;
            }

        }

        public ReturnUsers DeleteUserByID(string userToDelete) 
        {
            ReturnUsers returnMesage = new ReturnUsers();
            if (Validator.IsValidAuthHead())
            {
                try
                {
                    string authHead = getAuthHeader();
                    string[] userPass = authHead.Split(new char[] { ':' });
                    string username = userPass[0];
                    string pass = userPass[1];
                    
                    using (var context = new WeatherAppContext())
                    {
                        var result = context.UserEntities.Where(c => c.UserName == userToDelete).FirstOrDefault();

                        if (result == null)
                        {
                            returnMesage.Success = "1";
                            returnMesage.ErrorMessage = "No user found with that id";
                            return returnMesage;
                            //no user
                        }

                        else
                        {
                            //check whether its admin who is making the request
                            var admin = context.UserEntities.Where(c => c.UserName.Equals("admin")).FirstOrDefault();
                            if (username.Equals(admin.UserName))
                            {
                                bool validAdminPass = PasswordHash.ValidatePassword(pass, admin.Password);                                
                                if (validAdminPass)
                                {
                                    if (result.UserName.Equals(admin.UserName))
                                    {
                                        //cannot delete admin
                                        returnMesage.Success = "2";
                                        returnMesage.ErrorMessage = "Can't delete Admin! ";
                                        return returnMesage;
                                    }
                                    else
                                    {
                                        //gett asocciated posts
                                        var postsByUser = context.PostEntities.Where(c => c.UserName.Equals(result.UserName)).ToList();
                                        foreach (var item in postsByUser)
                                        {
                                            //delete asocciated posts
                                            context.PostEntities.Remove(item);
                                        }

                                        //delete user
                                        context.UserEntities.Remove(result);
                                        context.SaveChanges();

                                        returnMesage.Success = "0";
                                        returnMesage.ErrorMessage = "Successfully deleted user and posts associated ";
                                        return returnMesage;
                                    }
                                }
                                else
                                {
                                    WebOperationContext.Current.OutgoingResponse.StatusCode =
                                                                HttpStatusCode.Unauthorized;
                                    returnMesage.Success = "3";
                                    returnMesage.ErrorMessage = "Unauthorized attempt";
                                    return returnMesage;
                                    //wrong password
                                }
                            }
                            else
                            {
                                WebOperationContext.Current.OutgoingResponse.StatusCode =
                                                                   HttpStatusCode.Unauthorized;
                                returnMesage.Success = "3";
                                returnMesage.ErrorMessage = "Unauthorized attempt";
                                return returnMesage;
                            }
                        }

                    }
                }
                catch (Exception e)
                {
                    returnMesage.Success = "3";
                    returnMesage.ErrorMessage = e.Message;
                    return returnMesage;
                }
            }
            else
            {
                WebOperationContext.Current.OutgoingResponse.StatusCode =
                     HttpStatusCode.Unauthorized;
                return returnMesage;
            }

        }

        public bool doesEmailExists(string email)
        {
            using (var context = new WeatherAppContext())
            {
                if (context.UserEntities.Any(o => o.Email == email))
                {
                    // Match!
                    return true;
                }
                return false;
            }
        }        

        public string getAuthHeader()
        {
            WebOperationContext ctx = WebOperationContext.Current;
            string authStr = ctx.IncomingRequest.Headers[HttpRequestHeader.Authorization];
            string encodedCredentials = authStr.Substring(6);

            byte[] decodedBytes =
            Convert.FromBase64String(encodedCredentials);
            string s = new ASCIIEncoding().GetString(decodedBytes);
            return s;
        }        

    }
}
